package cn.xiuxius.embedded.toolwindow;

import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.Splitter;
import com.intellij.ui.JBColor;
import com.intellij.ui.components.*;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.WrapLayout;
import org.apache.commons.lang3.ArrayUtils;

import javax.swing.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;

public class SerialTerminalPanel {
    private final JBPanel<?> root;
    private final JTextPane recvArea;
    private final JBTextArea sendArea;
    private final JBCheckBox hexSend, hexRecv, addEndCode;
    private final JBCheckBox autoSend;
    private final JBTextField intervalField;
    private Timer timer;
    private SerialPort port;
    private final Splitter splitter;
    private final JBScrollPane recvSp, sendSp;
    private final JComboBox<Charset> encodingBox;

    public SerialTerminalPanel(String name, int baud, int dataBits, int parity, int stopBits, Runnable onClose) {
        root = new JBPanel<>(new BorderLayout(6, 6));
        root.setBorder(JBUI.Borders.empty(10));
        root.setBackground(JBColor.PanelBackground);
        root.setPreferredSize(new Dimension(150, 400)); // 你可以根据需要设置默认大小

        // 标题栏 Panel
        JBPanel<?> hdr = new JBPanel<>(new BorderLayout(6, 6));
        hdr.setOpaque(false);

        // 串口名称 Label（左侧）
        JBLabel lbl = new JBLabel("串口：" + name);
        lbl.setFont(lbl.getFont().deriveFont(Font.BOLD));
        hdr.add(lbl, BorderLayout.WEST);

        // 编码选择下拉框初始化（先于使用）
        ArrayList<Charset> charsetList = new ArrayList<>();
        charsetList.add(null); // 平台默认
        charsetList.add(StandardCharsets.UTF_8);
        charsetList.add(StandardCharsets.ISO_8859_1);
        charsetList.add(StandardCharsets.US_ASCII);
        charsetList.add(Charset.forName("GBK"));
        encodingBox = new ComboBox<>(charsetList.toArray(new Charset[0]));
        encodingBox.setPreferredSize(new Dimension(90, 30));
        encodingBox.setRenderer(new DefaultListCellRenderer() {
            @Override
            public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
                String display = (value == null) ? "默认" : ((Charset) value).name();
                return super.getListCellRendererComponent(list, display, index, isSelected, cellHasFocus);
            }
        });
        encodingBox.setSelectedIndex(0); // 默认选择平台默认

        // 右侧区域：编码选择 + 关闭按钮（垂直排列）
        JBPanel<?> rightPanel = new JBPanel<>();
        rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS));
        rightPanel.setOpaque(false);

// 编码选择面板
        JBPanel<?> encodingPanel = new JBPanel<>(new FlowLayout(FlowLayout.RIGHT, 5, 0));
        encodingPanel.setOpaque(false);
        encodingPanel.add(new JLabel("编码"));
        encodingPanel.add(encodingBox);

// 关闭按钮面板
        JBPanel<?> closePanel = new JBPanel<>(new FlowLayout(FlowLayout.RIGHT, 5, 0));
        closePanel.setOpaque(false);
        JButton closeBtn = new JButton("关闭");
        closeBtn.addActionListener(e -> {
            closePort();
            onClose.run();
        });
        closePanel.add(closeBtn);

// 组装右侧面板
        rightPanel.add(encodingPanel);
        rightPanel.add(Box.createVerticalStrut(5)); // 垂直间距
        rightPanel.add(closePanel);

// 添加右侧面板到标题栏
        hdr.add(rightPanel, BorderLayout.EAST);


        // 添加标题栏到 root
        root.add(hdr, BorderLayout.NORTH);

        // 接收区
        recvArea = new JTextPane() {
            @Override
            public boolean getScrollableTracksViewportWidth() {
                return true;
            }

            @Override
            public boolean getScrollableTracksViewportHeight() {
                return false;
            }
        };
        recvArea.setEditable(false);
        recvArea.setBackground(JBColor.WHITE);
        recvArea.setFont(recvArea.getFont().deriveFont(14f));
        recvSp = new JBScrollPane(recvArea);
        recvSp.setMinimumSize(new Dimension(10, getLineHeight(recvArea)));
        recvSp.setPreferredSize(new Dimension(0, 200));
        recvSp.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));

        // 发送区
        sendArea = new JBTextArea();
        sendArea.setFont(sendArea.getFont().deriveFont(14f));
        sendArea.setLineWrap(true);
        sendArea.setWrapStyleWord(true);
        sendSp = new JBScrollPane(sendArea);
        sendSp.setMinimumSize(new Dimension(10, getLineHeight(sendArea)));
        sendSp.setPreferredSize(new Dimension(0, getLineHeight(sendArea) * 3));
        sendSp.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));

        // Splitter：垂直分割
        splitter = new Splitter(true, 0.7f);
        splitter.setFirstComponent(recvSp);
        splitter.setSecondComponent(sendSp);

        // 监听root大小，调整最大高度限制
        root.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                updateSizeLimits();
            }
        });

        // 限制 splitter 拖动比例，防止一边太小或太大
        splitter.addPropertyChangeListener("proportion", evt -> {
            double proportion = splitter.getProportion();
            double minProp = (double) getLineHeight(recvArea) / root.getHeight();
            double maxProp = 1.0 - (double) getLineHeight(sendArea) / root.getHeight();
            if (proportion < minProp) {
                splitter.setProportion((float) minProp);
            } else if (proportion > maxProp) {
                splitter.setProportion((float) maxProp);
            }
        });

        root.add(splitter, BorderLayout.CENTER);

        // 操作面板
        JBPanel<?> op = new JBPanel<>();
        op.setLayout(new WrapLayout(FlowLayout.LEFT, 10, 5));

        hexSend = new JBCheckBox("HEX 发");
        hexRecv = new JBCheckBox("HEX 收");
        addEndCode = new JBCheckBox("结束符");
        autoSend = new JBCheckBox("自动发");
        intervalField = new JBTextField(5);
        intervalField.setText("1000");
        intervalField.setEnabled(false);

        autoSend.addActionListener(e -> {
            boolean selected = autoSend.isSelected();
            intervalField.setEnabled(selected);
            if (!selected) {
                handleTimer(false);  // 取消勾选时停止定时器
            }
        });

        JButton sendBtn = new JButton("发送");
        JButton clearBtn = new JButton("清除");
        clearBtn.addActionListener(e -> recvArea.setText(""));

        op.add(hexSend);
        op.add(hexRecv);
        op.add(addEndCode);
        op.add(autoSend);
        op.add(intervalField);
        op.add(clearBtn);
        op.add(sendBtn);

        root.add(op, BorderLayout.SOUTH);

        // Ctrl + 滚轮字体缩放
        recvSp.addMouseWheelListener(e -> {
            if (e.isControlDown()) {
                Font f = recvArea.getFont();
                float size = f.getSize2D() + (e.getWheelRotation() < 0 ? 1 : -1);
                recvArea.setFont(f.deriveFont(Math.max(8, size)));
                updateSizeLimits();
            }
        });
        sendSp.addMouseWheelListener(e -> {
            if (e.isControlDown()) {
                Font f = sendArea.getFont();
                float size = f.getSize2D() + (e.getWheelRotation() < 0 ? 1 : -1);
                sendArea.setFont(f.deriveFont(Math.max(8, size)));
                updateSizeLimits();
            }
        });

        sendBtn.addActionListener(e -> {
            send();
            if (autoSend.isSelected()) {
                handleTimer(true);
            }
        });

        sendArea.addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e2) {
                if (e2.getKeyCode() == KeyEvent.VK_ENTER && !e2.isShiftDown()) {
                    send();
                    if (autoSend.isSelected()) {
                        handleTimer(true);
                    }
                    e2.consume();
                }
            }
        });

        initPort(name, baud, dataBits, parity, stopBits);
        if (port != null && port.isOpen()) {
            startListening();
            if (autoSend.isSelected()) {
                handleTimer(true);
            }
        }
    }

    private void updateSizeLimits() {
        int rootHeight = root.getHeight();
        if (rootHeight <= 0) return;

        int maxHeight = (int) (rootHeight * 0.95);

        int recvMinHeight = getLineHeight(recvArea);
        int sendMinHeight = getLineHeight(sendArea);

        recvSp.setMinimumSize(new Dimension(10, recvMinHeight));
        sendSp.setMinimumSize(new Dimension(10, sendMinHeight));

        recvSp.setMaximumSize(new Dimension(Integer.MAX_VALUE, maxHeight));
        sendSp.setMaximumSize(new Dimension(Integer.MAX_VALUE, maxHeight));

        // 重新设置 splitter 限制
        double minProp = (double) recvMinHeight / rootHeight;
        double maxProp = 1.0 - (double) sendMinHeight / rootHeight;
        double prop = splitter.getProportion();
        if (prop < minProp) prop = minProp;
        if (prop > maxProp) prop = maxProp;
        splitter.setProportion((float) prop);

        root.revalidate();
        root.repaint();
    }

    private int getLineHeight(JTextComponent area) {
        FontMetrics fm = area.getFontMetrics(area.getFont());
        return fm.getHeight() + 4; // 4像素padding，保证有点空隙
    }

    private void initPort(String name, int baud, int db, int pr, int sb) {
        port = SerialPort.getCommPort(name);
        port.setComPortParameters(baud, db, sb, pr);
        port.setComPortTimeouts(SerialPort.TIMEOUT_NONBLOCKING, 0, 0);
        if (!port.openPort()) {
            Messages.showErrorDialog("打开失败：" + name, "错误");
            port = null;
        }
    }

    private void startListening() {
        port.addDataListener(new SerialPortDataListener() {
            @Override
            public int getListeningEvents() {
                return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
            }

            @Override
            public void serialEvent(SerialPortEvent event) {
                if (event.getEventType() != SerialPort.LISTENING_EVENT_DATA_AVAILABLE) return;
                int available = port.bytesAvailable();
                if (available <= 0) return;
                byte[] buf = new byte[available];
                int read = port.readBytes(buf, buf.length);
                if (read > 0) {
                    String time = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss.SSS"));
                    String txt = hexRecv.isSelected() ? toHex(buf) : new String(buf, 0, read, getSelectedCharset());
                    append("[" + time + "] -> " + txt + "\n", JBColor.GREEN);
                }
            }
        });
    }

    private void send() {
        if (port == null || !port.isOpen()) return;
        String t = sendArea.getText().trim();
        if (t.isEmpty()) return;
        byte[] d;
        try {
            d = hexSend.isSelected() ? hexToBytes(t.replaceAll("\\s", "")) : t.getBytes(StandardCharsets.UTF_8);
        } catch (Exception e) {
            Messages.showErrorDialog("发送数据格式错误", "错误");
            return;
        }
        append("<- " + (hexSend.isSelected() ? toHex(d) : t) + "\n", JBColor.BLUE);
        if (addEndCode.isSelected()) {
            byte[] endCode = new byte[]{0x0D, 0x0A};
            d = ArrayUtils.addAll(d, endCode);
        }
        port.writeBytes(d, d.length);
    }

    private void append(String text, Color color) {
        SwingUtilities.invokeLater(() -> {
            StyledDocument doc = recvArea.getStyledDocument();
            Style style = recvArea.addStyle("ColorStyle", null);
            StyleConstants.setForeground(style, color);
            try {
                doc.insertString(doc.getLength(), text, style);
                recvArea.setCaretPosition(doc.getLength());
            } catch (BadLocationException e) {
                e.printStackTrace();
            }
        });
    }

    private void handleTimer(boolean start) {
        if (timer != null) {
            timer.stop();
            timer = null;
        }
        if (start) {
            int interval;
            try {
                interval = Integer.parseInt(intervalField.getText());
                if (interval < 50) interval = 50;
            } catch (NumberFormatException e) {
                interval = 1000;
            }
            timer = new Timer(interval, e -> send());
            timer.start();
        }
    }

    private void closePort() {
        if (timer != null) {
            timer.stop();
            timer = null;
        }
        if (port != null && port.isOpen()) {
            port.closePort();
        }
    }

    private static String toHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) sb.append(String.format("%02X ", b));
        return sb.toString().trim();
    }

    private static byte[] hexToBytes(String hex) {
        int len = hex.length();
        if (len % 2 != 0) throw new IllegalArgumentException("hex string length must be even");
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
                    + Character.digit(hex.charAt(i + 1), 16));
        }
        return data;
    }

    public JBPanel<?> getPanel() {
        return root;
    }

    public boolean isOpen() {
        return port != null && port.isOpen();
    }

    private Charset getSelectedCharset() {
        Charset cs = (Charset) encodingBox.getSelectedItem();
        return cs != null ? cs : Charset.defaultCharset();
    }

}
